---------------------------------------------------------------------------------------
-- HTTP/1.1 client support for NetStreams "stream" API
-- Adapted from LuaSocket 1.4 toolkit.
-- Author:  Josh Kewley
-- Company: Insario Corporation
--          Scottsdale, AZ
-- Date: 03/27/2008
---------------------------------------------------------------------------------------
local Private = require "httpPrivate"
local URL = require "url"

module(...,package.seeall)

RevisionNumber="0.63"

debug("status", "Http private library version "..Private.RevisionNumber)

function DB(message)
    debug("Http", makeReadable(message))
end

function DBD(message)
    debug("HttpDetail", makeReadable(message))
end

-- We want to be able to see the carriage returns and newlines in the debugger
function makeReadable(debugString)
  if (debugString==nil) then return "" end
  return debugString:gsub("\n" , "\\n"):gsub("\r" , "\\r")
end

-----------------------------------------------------------------------------
-- Sends a HTTP request and retrieves the server reply
-- Input
--  request: a table with the following fields
--    method: "GET", "PUT", "POST" etc (defaults to "GET")
--    url: request URL, i.e. the document to be retrieved
--    user, password: authentication information
--    headers: request header fields, or nil if none
--    body: request message body as a string, or nil if none
--    stay: should we refrain from following a server redirect message?
-- Returns
--  response: a table with the following fields:
--    body: response message body, or nil if failed
--    headers: response header fields, or nil if failed
--    status: server response status line, or nil if failed
--    code: server response status code, or nil if failed
--    error: error message if any
-----------------------------------------------------------------------------
function IssueRequest(aRequestTable)
    local response = {}
    local responseBody = {}
    response.buildBodyCallback = function(chunk, err)
        DB("Response body callback ("..string.len(chunk).." bytes) "..chunk)
        if err then 
          DB("Can't build body. Error reported: "..err)
          return 0, err
        end
        if chunk then responseBody[#responseBody+1]=chunk end
        return string.len(chunk)
    end
    -- when this is asynchronous, we'll have to poll for results
    Private.ProcessRequest(aRequestTable, response)
    while (Private.ResponseComplete==0) do
      DB("Sleeping while request processes")
      os.sleep(500)
    end
    
    response.body = table.concat(responseBody)
    response.buildBodyCallback = nil
    return response
end

-----------------------------------------------------------------------------
-- Retrieves a URL by the method "GET"
-- Input
--  url_or_request: target url or request table (a table with the fields:
--    url: the target URL
--    user: account user name
--    pass: account password)
-- Returns
--  body: response message body, or nil if failed and handled
--  headers: response header fields received, or nil if failed
--  code: server response status code, or nil if failed
--  error: error message if any
-----------------------------------------------------------------------------
function Get(url_or_request)
    local request = Private.BuildRequestTable(url_or_request)
    DB("Getting "..request.url)
    request.method = "GET"
    local response = IssueRequest(request)
    return response.body, response.headers, response.code, response.error
end

-----------------------------------------------------------------------------
-- Retrieves a URL by the method "POST"
-- Input
--  url_or_request: target url or request table (a table with the fields:
--    url: the target URL
--    body: request message body
--    user: account user name
--    pass: account password)
--  body: request message body, or nil if none
-- Returns
--   body: response message body, or nil if failed
--   headers: response header fields received, or nil if failed
--   code: server response status code, or nil if failed
--   error: error message, or nil if successfull
-----------------------------------------------------------------------------
function Post(url_or_request, body)
    local request = Private.BuildRequestTable(url_or_request)
    DB("POSTing "..request.url)
    request.method = "POST"
    request.body = request.body or body
    local response = IssueRequest(request)
    return response.body, response.headers, response.code, response.error
end